package com.zjuh.taofood.biz.ao.impl;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;

import wint.lang.utils.CollectionUtil;
import wint.lang.utils.MapUtil;
import wint.lang.utils.StringUtil;

import com.taobao.api.ApiException;
import com.taobao.api.domain.Item;
import com.taobao.api.domain.ItemCat;
import com.taobao.api.domain.TaobaokeItem;
import com.taobao.api.domain.TradeRate;
import com.taobao.api.domain.User;
import com.zjuh.taofood.biz.ao.WorkerAO;
import com.zjuh.taofood.biz.bo.ItemConverter;
import com.zjuh.taofood.biz.bo.PagedResult;
import com.zjuh.taofood.biz.bo.TaobaoOpenBO;
import com.zjuh.taofood.biz.bo.TaokeUtil;
import com.zjuh.taofood.biz.bo.TradeRateBO;
import com.zjuh.taofood.biz.bo.impl.TradeRateBOImpl;
import com.zjuh.taofood.biz.dao.ActiveTopicItemDAO;
import com.zjuh.taofood.biz.dao.CategoryDAO;
import com.zjuh.taofood.biz.dao.ProccessStatusDAO;
import com.zjuh.taofood.biz.dao.TopicDAO;
import com.zjuh.taofood.biz.dao.TopicItemDAO;
import com.zjuh.taofood.biz.dao.query.ActiveTopicItemQuery;
import com.zjuh.taofood.biz.dao.query.TopicItemQuery;
import com.zjuh.taofood.biz.dao.query.TopicQuery;
import com.zjuh.taofood.biz.domain.ActiveTopicItemDO;
import com.zjuh.taofood.biz.domain.CategoryDO;
import com.zjuh.taofood.biz.domain.ProccessStatusDO;
import com.zjuh.taofood.biz.domain.TopicDO;
import com.zjuh.taofood.biz.domain.TopicItemDO;

public class WorkerAOImpl extends BaseAO implements WorkerAO {

	private TaobaoOpenBO taobaoOpenBO;
	
	private TopicItemDAO topicItemDAO;
	
	private CategoryDAO categoryDAO;
	
	private ProccessStatusDAO proccessStatusDAO;
	
	private ActiveTopicItemDAO activeTopicItemDAO;
	
	private TopicDAO topicDAO;
	
	private TradeRateBO tradeRateBO = new TradeRateBOImpl();
	
	private Random rand = new Random();
	
	//35  奶粉/辅食/营养品, true
	//50022517, 孕妇装/孕产妇用品/营养, true
	//50020276, 品牌保健品, false
	//50020275, 传统滋补品/其他保健营养品, true
	//50002766, 零食/坚果/茶叶/特产, true
	//50016422, 粮油/蔬果/水产/速食, true
	//50008075, 吃喝玩乐折扣券, true
	//50026316, 茶/酒/冲饮, true
	private List<Long> catIds = Arrays.asList(35L,  50002766L, 50016422L, 50008075L, 50026316L);
	
	private int proccessPages = 3;
	
	/**
	 * 最小销售量
	 */
	private int leastVolume = 20;
	
	private int maxTaokeIdCount = 39;
	
	private List<String> filtersWords = CollectionUtil.newArrayList();
	
	private static Pattern filterPattern = Pattern.compile("\\&.*?;");
	
	public WorkerAOImpl() {
		filtersWords.add("卖家");
		filtersWords.add("QQ");
		filtersWords.add("qq");
		filtersWords.add("现货");
		filtersWords.add("物流");
		filtersWords.add("差评");
		filtersWords.add("不舒服");
		filtersWords.add("坑爹");
		filtersWords.add("不好吃");
		filtersWords.add("不给力");
		filtersWords.add("店铺");
		filtersWords.add("不爽");
		filtersWords.add("小店");
		filtersWords.add("太差");
		filtersWords.add("不开心");
		filtersWords.add("不好");
		filtersWords.add("光临");
	}
	
	
	
	public void runTopTopicItems() {
		try {
			log.info("====runTopTopicItems starting=====");
			long topicId = 9;
			TopicItemQuery topicItemQuery = new TopicItemQuery();
			topicItemQuery.setPageNo(1);
			topicItemQuery.setPageSize(100);
			List<TopicItemDO> topicItemDOs = topicItemDAO.queryTopTopicItems(topicItemQuery);
			
			activeTopicItemDAO.deleteByTopicId(topicId);
			
			for (TopicItemDO topicItemDO : topicItemDOs) {
				ActiveTopicItemDO activeTopicItemDO = ItemConverter.toActiveTopicItemDO(topicItemDO, topicId);
				activeTopicItemDAO.create(activeTopicItemDO);
			}
			
			topicDAO.updateItemCount(topicId, topicItemDOs.size());
			
			log.info("====runTopTopicItems success=====");
		} catch (Exception e) {
			log.error("runTopTopicItems failed", e);
		}
	}
	
	private AtomicBoolean runningUpdateRates = new AtomicBoolean(false);
	
	public void runUpdateRates() {
		if (!runningUpdateRates.compareAndSet(false, true)) {
			log.info("runUpdateRates has been started!");
			return;
		}
		try {
			long ts = System.currentTimeMillis();
			log.info("====runUpdateRates starting=====");
			int topicPageNum = 1;
			while (true) {
				TopicQuery topicQuery = new TopicQuery();
				topicQuery.setPageNo(topicPageNum);
				topicQuery.setPageSize(100);			
				List<TopicDO> topics = topicDAO.queryForPage(topicQuery);
				if (CollectionUtil.isEmpty(topics)) {
					break;
				}
				for (TopicDO topicDO : topics) {
					processTopicActiveItems(topicDO.getId());
				}
				
				++topicPageNum;
			}
			
			log.info("====runUpdateRates success=====");
			long delta = System.currentTimeMillis() - ts;
			log.info("escape "+ (delta / 1000) +"seconds");
		} catch (Exception e) {
			log.error("runUpdateRates failed", e);
		} finally {
			runningUpdateRates.set(false);
		}
	}
	
	private void processTopicActiveItems(long topicId) throws ApiException {
		int pageNum = 1;
		while (true) {
			ActiveTopicItemQuery activeTopicItemQuery = new ActiveTopicItemQuery();
			activeTopicItemQuery.setTopicId(topicId);
			activeTopicItemQuery.setPageSize(40);
			activeTopicItemQuery.setPageNo(pageNum);
			List<ActiveTopicItemDO> topicItems = activeTopicItemDAO.queryByTopicId(activeTopicItemQuery);
			if (CollectionUtil.isEmpty(topicItems)) {
				break;
			}
			
			List<String> nicks = CollectionUtil.newArrayList();
			for (ActiveTopicItemDO activeTopicItem : topicItems) {
				nicks.add(activeTopicItem.getSellerNick());
			}
			
			Map<String, User> users = taobaoOpenBO.getUserByNicks(nicks);
			for (ActiveTopicItemDO activeTopicItem : topicItems) {
				processTradeRates(activeTopicItem, users);
				if (!StringUtil.isEmpty(activeTopicItem.getTradeRatesSnapshot())) {
					activeTopicItemDAO.update(activeTopicItem);
				}
				
			}
			
			++pageNum;
		}
	}
	
	private void updateProccessStatus(long cid, int page) {
		ProccessStatusDO inDb = proccessStatusDAO.queryByCid(cid);
		if (inDb == null) {
			ProccessStatusDO proccessStatusDO = new ProccessStatusDO();
			proccessStatusDO.setPage(page);
			proccessStatusDO.setTimes(0);
			proccessStatusDO.setCid(cid);
			proccessStatusDAO.create(proccessStatusDO);
		} else {
			inDb.setPage(page);
			proccessStatusDAO.update(inDb);
		}
	}
	
	private void incrProccessStatusTimes(long cid) {
		ProccessStatusDO inDb = proccessStatusDAO.queryByCid(cid);
		if (inDb == null) {
			ProccessStatusDO proccessStatusDO = new ProccessStatusDO();
			proccessStatusDO.setPage(1);
			proccessStatusDO.setTimes(0);
			proccessStatusDO.setCid(cid);
			proccessStatusDAO.create(proccessStatusDO);
		} else {
			inDb.setTimes(inDb.getTimes() + 1);
			inDb.setPage(1);
			proccessStatusDAO.update(inDb);
		}
	}
	
	private boolean needProccessCid(long cid) {
		ProccessStatusDO proccessStatusDO = proccessStatusDAO.queryByCid(cid);
		if (proccessStatusDO == null) {
			proccessStatusDO = new ProccessStatusDO();
			proccessStatusDO.setPage(1);
			proccessStatusDO.setTimes(0);
			proccessStatusDO.setCid(cid);
			proccessStatusDAO.create(proccessStatusDO);
			return true;
		}
		if (proccessStatusDO.getPage() < proccessPages) {
			return true;
		}
		return false;
	}
	
	private AtomicBoolean runningFetchItems = new AtomicBoolean(false);
	
	@Override
	public void runFetchItems() {
		if (!runningFetchItems.compareAndSet(false, true)) {
			log.info("runFetchItems has been started!");
			return;
		}
		try {
			List<ItemCat> itemCats = getItemCats();
			long count = 0;
			for (ItemCat itemCat : itemCats) {
				if (needProccessCid(itemCat.getCid())) {
					count += proccessCat(itemCat.getCid());
					log.info("proccess items: " + count);
				} else {
					log.info("need not proccess: " + itemCat.getCid());
				}
			}
			
			for (ItemCat itemCat : itemCats) {
				incrProccessStatusTimes(itemCat.getCid());
			}
			
			log.info("proccess runFetchItems finish! item count: " + count);
		} catch (Exception e) {
			log.error("runFetchItems", e);
		} finally {
			runningFetchItems.set(false);
		}
	}
	
	private int proccessCat(long cid) throws ApiException {
		int count = 0;
		for (int page = 1; page <= proccessPages; ++page) {
			List<Item> items = taobaoOpenBO.searchItems(cid, page);
			List<Long> itemIds = filterItems(items);
			count += proccessItems(itemIds, cid, page);
			if (log.isInfoEnabled()) {
				log.info("cid: " + cid + ", count:" + count);
			}
		}
		return count;
	}
	
	private List<Long> filterItems(List<Item> items) {
		List<Long> ids = CollectionUtil.newArrayList();
		for (Item item : items) {
			if (item.getVolume() < leastVolume) {
				continue;
			}
			ids.add(item.getNumIid());
		}
		return ids;
	}
	
	private int proccessItems(List<Long> itemIds, long cid, int page) throws ApiException {
		int count = 0;
		try {
			if (CollectionUtil.isEmpty(itemIds)) {
				return 0;
			}
			List<Long> targetItemIds = CollectionUtil.newArrayList();
			
			
			for (Long itemId : itemIds) {
				targetItemIds.add(itemId);
				if (targetItemIds.size() >= maxTaokeIdCount) {
					List<TaobaokeItem> taobaokeItems =  taobaoOpenBO.queryTaobaokeItems(targetItemIds);
					List<TopicItemDO> topicItemDOs = TaokeUtil.toTopicItems(taobaokeItems);
					for (TopicItemDO topicItemDO : topicItemDOs) {
						topicItemDO.setCid(cid);
					}
					for (TopicItemDO topicItemDO : topicItemDOs) {
						insertOrUpdateItem(topicItemDO);
						++count;
					}
					targetItemIds = CollectionUtil.newArrayList();
				}
			}
		} finally {
			updateProccessStatus(cid, page);
		}
		return count;
	}
	
	private long lastVisited = 0;
	
	private void processTradeRates(ActiveTopicItemDO activeTopicItemDO, Map<String, User> users) throws ApiException {
		try {
			User user = users.get(activeTopicItemDO.getSellerNick());
			if (user == null) {
				return;
			}
			Long userId = user.getUserId();
			if (userId == null) {
				return;
			}
			
			List<TradeRate> tradeRates = CollectionUtil.newArrayList();
			int tradeRatesCount = 0;
			PagedResult<TradeRate> pagedResult = null;
			int page = 1;
			
			while (true) {
				// ========================================================
				// 为了防止访问过于频繁，如果访问过于频繁，淘宝会自动屏蔽
				while (System.currentTimeMillis() - lastVisited < 1000) {
					Thread.sleep(100);	
				}
				lastVisited = System.currentTimeMillis();
				// =========================================================
				
				pagedResult = tradeRateBO.searchTraderates(userId, activeTopicItemDO.getItemId(), page);
				tradeRates.addAll(pagedResult.getResults());
				tradeRatesCount = tradeRates.size();
				if (page >= pagedResult.getMaxPage()) {
					break;
				}
				if (page > 3) {
					tradeRatesCount = pagedResult.getResults().size() * (pagedResult.getMaxPage() - 1) + rand.nextInt(10);
					break;
				}
				page++;	
			}
			
			activeTopicItemDO.setTradeRatesCount(tradeRatesCount);
			activeTopicItemDO.setTradeRatesSnapshot(getTradeRateSnapshots(tradeRates));
		} catch (Exception e) {
			log.error("get rate failed", e);
		}
	}
	
	private String getTradeRateSnapshots(List<TradeRate> tradeRates) {
		if (tradeRates == null) {
			return null;
		}
		List<TradeRate> filteredTradeRates = filterTradeRates(tradeRates);
		Collections.sort(filteredTradeRates, new Comparator<TradeRate>() {

			@Override
			public int compare(TradeRate o1, TradeRate o2) {
				return o2.getContent().length() - o1.getContent().length();
			}
		});
		if (CollectionUtil.isEmpty(filteredTradeRates)) {
			return null;
		}
		if (filteredTradeRates.size() >= 2) {
			return proccessTradeRateContent(filteredTradeRates.get(0).getContent() + " " + filteredTradeRates.get(1).getContent());
		}
		if (filteredTradeRates.size() >= 1) {
			return proccessTradeRateContent(filteredTradeRates.get(0).getContent());
		}
		return null;
	}
	
	private static String proccessTradeRateContent(String tradeRate) {
		tradeRate = StringUtil.replace(tradeRate, "<br/>", " ");
		tradeRate = filterPattern.matcher(tradeRate).replaceAll("");
		return StringUtil.trimToSize(tradeRate, 1000);
	}

	private List<TradeRate> filterTradeRates(List<TradeRate> tradeRates) {
		List<TradeRate> ret = CollectionUtil.newArrayList();
		for (TradeRate tradeRate : tradeRates) {
			String content = tradeRate.getContent();
			if (StringUtil.isEmpty(content)) {
				continue;
			}
			
			if (!isContentOk(content)) {
				continue;
			}
			ret.add(tradeRate);
		}
		return ret;
	}
	
	private boolean isContentOk(String content) {
		if (content.length() < 12) {
			return false;
		}
		for (String word : filtersWords) {
			if (content.contains(word)) {
				return false;
			}
		}
		
		Map<Character, Integer> chars = MapUtil.newHashMap();
		int totalCount = 0;
		int maxCharCount = 0;
		for (int i = 0, len = content.length(); i < len; ++i) {
			char c = content.charAt(i);
			Integer ct = chars.get(c);
			if (ct == null) {
				chars.put(c, 1);
				ct = 1;
			} else {
				chars.put(c, ct + 1);
			}
			
			maxCharCount = Math.max(maxCharCount, ct);
			++totalCount;
		}
		if (maxCharCount/(double)totalCount >= 0.5) {
			// 一般以上的字符是同一个字符
			return false;
		}
		return true;
	}
	
	private void insertOrUpdateItem(TopicItemDO topicItemDO) {
		TopicItemDO inDb = topicItemDAO.queryByItemId(topicItemDO.getItemId());
		if (inDb == null) {
			topicItemDAO.create(topicItemDO);
		} else {
			inDb.setCommission(topicItemDO.getCommission());
			inDb.setCommissionRate(topicItemDO.getCommissionRate());
			inDb.setClickUrl(topicItemDO.getClickUrl());
			inDb.setItemDetailUrl(topicItemDO.getItemDetailUrl());
			inDb.setItemPicUrl(topicItemDO.getItemPicUrl());
			inDb.setItemTitle(topicItemDO.getItemTitle());
			inDb.setPrice(topicItemDO.getPrice());
			inDb.setSellerCreditScore(topicItemDO.getSellerCreditScore());
			inDb.setSellerNick(topicItemDO.getSellerNick());
			inDb.setItemId(topicItemDO.getItemId());
			inDb.setVolume(topicItemDO.getVolume());
			inDb.setCid(topicItemDO.getCid());
			inDb.setTradeRatesCount(topicItemDO.getTradeRatesCount());
			inDb.setTradeRatesSnapshot(topicItemDO.getTradeRatesSnapshot());
			topicItemDAO.update(inDb);
			
			proccessActiveTopicItemDO(inDb);
		}
	}
	
	private void proccessActiveTopicItemDO(TopicItemDO topicItemDO) {
		List<ActiveTopicItemDO>  activeTopicItemDOs = activeTopicItemDAO.queryByItemId(topicItemDO.getItemId());
		for (ActiveTopicItemDO activeTopicItemDO : activeTopicItemDOs) {
			activeTopicItemDO.setPrice(topicItemDO.getPrice());
			activeTopicItemDO.setItemPicUrl(topicItemDO.getItemPicUrl());
			activeTopicItemDO.setSellerCreditScore(topicItemDO.getSellerCreditScore());
			activeTopicItemDO.setTitle(topicItemDO.getItemTitle());
			activeTopicItemDO.setTradeRatesCount(topicItemDO.getTradeRatesCount());
			activeTopicItemDO.setTradeRatesSnapshot(topicItemDO.getTradeRatesSnapshot());
			activeTopicItemDO.setVolume(topicItemDO.getVolume());
			activeTopicItemDAO.update(activeTopicItemDO);
		}
	}
	
	private List<ItemCat> getItemCats() throws ApiException {
		List<ItemCat> itemCats = CollectionUtil.newArrayList();
		for (long catId : catIds) {
			getItemCatsImpl(itemCats, catId);
		}
		return itemCats;
	}
	
	private void getItemCatsImpl(List<ItemCat> itemCats, long parentId) throws ApiException {
		List<ItemCat> resultItemCats = taobaoOpenBO.getItemCatsByParentId(parentId);
		if (CollectionUtil.isEmpty(resultItemCats)) {
			return;
		}
		for (ItemCat itemCat : resultItemCats) {
			itemCats.add(itemCat);
			if (itemCat.getIsParent()) {
				getItemCatsImpl(itemCats, itemCat.getCid());
			}
		}
	}

	@Override
	public void runFetchCategories() {
		try {
			List<ItemCat> itemCats = this.getItemCats();
			for (ItemCat itemCat : itemCats) {
				CategoryDO cat = TaokeUtil.toCategory(itemCat);
				insertOrUpdateCategory(cat);
			}
		} catch (Exception e) {
			log.error("runFetchCategories error", e);
		}
	}
	
	private void insertOrUpdateCategory(CategoryDO categoryDO) {
		CategoryDO inDb = categoryDAO.queryByCid(categoryDO.getCid());
		if (inDb == null) {
			categoryDAO.create(categoryDO);
		} else {
			inDb.setName(categoryDO.getName());
			inDb.setParentCid(categoryDO.getParentCid());
			categoryDAO.update(inDb);
		}
	}

	public void setTaobaoOpenBO(TaobaoOpenBO taobaoOpenBO) {
		this.taobaoOpenBO = taobaoOpenBO;
	}

	public void setTopicItemDAO(TopicItemDAO topicItemDAO) {
		this.topicItemDAO = topicItemDAO;
	}

	public void setCategoryDAO(CategoryDAO categoryDAO) {
		this.categoryDAO = categoryDAO;
	}

	public void setProccessStatusDAO(ProccessStatusDAO proccessStatusDAO) {
		this.proccessStatusDAO = proccessStatusDAO;
	}

	public void setActiveTopicItemDAO(ActiveTopicItemDAO activeTopicItemDAO) {
		this.activeTopicItemDAO = activeTopicItemDAO;
	}

	public void setTopicDAO(TopicDAO topicDAO) {
		this.topicDAO = topicDAO;
	}
}
